`include "./parameters.v"

module keccak(
	input	clk,
	input	rst_n,
	input	enable,
	input 	[1:0]	model,//0->sha3256 1->sha3512 2->shake128 3->shake256

	input 	[63:0]	data_in,

	output	reg	[1599:0]	data_buffer,

	input 	padding,
	input	read_en,
	input	absorb,
	input	squeeze,
	output	reg	hashdone);

parameter IDLE		=	4'd0,	READ 	=	4'd1,	PADDING =	4'd2,	WORKING	=	4'd3,	WAIT	=	4'd4;
	
	reg	[3:0]	state,nextstate;
	reg	[4:0]	absorb_cnt;
	reg	[4:0]	squeeze_cnt;

	wire		clear;

	reg	[6:0]	c1,c2;
//absorb/////////////////////////////////////////////////////////////
	
	wire	[1599:0]	data_buffer_a,data_buffer_b,data_buffer_c,data_buffer_d;


	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			data_buffer <= 1600'b0;
		else if(clear)
			data_buffer <= 1600'b0;
		else if(state == WORKING)
			data_buffer <= data_buffer_b;
		else if(state == PADDING & padding)
		begin
			case(model)
				`SHA3256 	:data_buffer[1087:1080] <= data_buffer[1087:1080] ^ 8'h80;
				`SHA3512 	:data_buffer[575:568]	<= data_buffer[575:568] ^ 8'h80;
				`SHAKE128 	:data_buffer[1343:1336] <= data_buffer[1343:1336] ^ 8'h80;
				`SHAKE256 	:data_buffer[1087:1080] <= data_buffer[1087:1080] ^ 8'h80;
			endcase // model
		end
		else if(read_en)
		begin
			case(absorb_cnt)
				5'd0:	data_buffer[64*1-1 :64*0] <= data_buffer[64*1-1 :64*0] ^ {data_in};
				5'd1:	data_buffer[64*2-1 :64*1] <= data_buffer[64*2-1 :64*1] ^ {data_in};
				5'd2:	data_buffer[64*3-1 :64*2] <= data_buffer[64*3-1 :64*2] ^ {data_in};
				5'd3:	data_buffer[64*4-1 :64*3] <= data_buffer[64*4-1 :64*3] ^ {data_in};
				5'd4:	data_buffer[64*5-1 :64*4] <= data_buffer[64*5-1 :64*4] ^ {data_in};
				5'd5:	data_buffer[64*6-1 :64*5] <= data_buffer[64*6-1 :64*5] ^ {data_in};
				5'd6:	data_buffer[64*7-1 :64*6] <= data_buffer[64*7-1 :64*6] ^ {data_in};
				5'd7:	data_buffer[64*8-1 :64*7] <= data_buffer[64*8-1 :64*7] ^ {data_in};
				5'd8:	data_buffer[64*9-1 :64*8] <= data_buffer[64*9-1 :64*8] ^ {data_in};
				5'd9:	data_buffer[64*10-1:64*9] <= data_buffer[64*10-1:64*9] ^ {data_in};
				5'd10:	data_buffer[64*11-1:64*10]<= data_buffer[64*11-1:64*10]^ {data_in};
				5'd11:	data_buffer[64*12-1:64*11]<= data_buffer[64*12-1:64*11]^ {data_in};
				5'd12:	data_buffer[64*13-1:64*12]<= data_buffer[64*13-1:64*12]^ {data_in};
				5'd13:	data_buffer[64*14-1:64*13]<= data_buffer[64*14-1:64*13]^ {data_in};
				5'd14:	data_buffer[64*15-1:64*14]<= data_buffer[64*15-1:64*14]^ {data_in};
				5'd15:	data_buffer[64*16-1:64*15]<= data_buffer[64*16-1:64*15]^ {data_in};
				5'd16:	data_buffer[64*17-1:64*16]<= data_buffer[64*17-1:64*16]^ {data_in};
				default:data_buffer <=	data_buffer;
			endcase // absorb_cnt
		end
		else
			data_buffer <= data_buffer;
	end
//keccak core/////////////////////////////////////////////////////////////

	// reg		[1599:0]	thopichiiota_data_buffer;
	// always@(posedge clk or negedge rst_n)
	// begin
	// 	if(!rst_n)
	// 		thopichiiota_data_buffer <= 1600'b0;
	// 	else
	// 		thopichiiota_data_buffer <= data_buffer_b;
	// end

	theta theta1(.in(data_buffer),
				 .out(data_buffer_a));

	thopichiiota thopichiiota1( .in (data_buffer_a),
								.out(data_buffer_b),
								.c  ({c1[6],31'b0,c1[5],15'b0,c1[4],7'b0,c1[3],3'b0,c1[2],1'b0,c1[1:0]}));

	// theta theta2(	.in 	(thopichiiota_data_buffer),
	// 				.out 	(data_buffer_c));

	// thopichiiota thopichiiota2(	.in (data_buffer_c),
	// 							.out(data_buffer_d),
	// 							.c  ({c2[6],31'b0,c2[5],15'b0,c2[4],7'b0,c2[3],3'b0,c2[2],1'b0,c2[1:0]}));

//FSM/////////////////////////////////////////////////////////////
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			state <= IDLE;
		else
			state <= nextstate;
	end

	always@(*)
	begin
		if(!enable)
			nextstate <= IDLE;
		else
			case(state)
				IDLE:	nextstate <= absorb ? READ : IDLE;
				READ:	nextstate <= absorb ? READ : PADDING;
				PADDING:nextstate <= WORKING;
				WORKING:nextstate <= hashdone?	WAIT : WORKING;
				WAIT:	nextstate <= absorb ? READ : squeeze ? WORKING : WAIT;
				default:nextstate <= IDLE;
			endcase // state
	end

	assign clear = state == IDLE;

	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			hashdone <= 1'b0;
		else
			hashdone <= squeeze_cnt == 5'd22;
	end

	// always@(*)
	// 	begin
	// 		case(squeeze_cnt)
	// 			5'd0:	begin	c1 <= 7'b0000_001;	c2 <= 7'b0011_010;end
	// 			5'd1:	begin	c1 <= 7'b1011_110;	c2 <= 7'b1110_000;end
	// 			5'd2:	begin	c1 <= 7'b0011_111;	c2 <= 7'b0100_001;end
	// 			5'd3:	begin	c1 <= 7'b1111_001;	c2 <= 7'b1010_101;end
	// 			5'd4:	begin	c1 <= 7'b0001_110;	c2 <= 7'b0001_100;end
	// 			5'd5:	begin	c1 <= 7'b0110_101;	c2 <= 7'b0100_110;end
	// 			5'd6:	begin	c1 <= 7'b0111_111;	c2 <= 7'b1001_111;end
	// 			5'd7:	begin	c1 <= 7'b1011_101;	c2 <= 7'b1010_011;end
	// 			5'd8:	begin	c1 <= 7'b1010_010;	c2 <= 7'b1001_000;end
	// 			5'd9:	begin	c1 <= 7'b0010_110;	c2 <= 7'b1100_110;end
	// 			5'd10:	begin	c1 <= 7'b1111_001;	c2 <= 7'b1011_000;end
	// 			5'd11:	begin	c1 <= 7'b0100_001;	c2 <= 7'b1110_100;end
	// 			default:begin	c1 <= 7'b0000_000;	c2 <= 7'b0000_000;end
	// 		endcase // squeeze_cnt
	// 	end

	always@(*)
		begin
			case(squeeze_cnt)
				5'd0:	begin	c1 <= 7'b0000_001;end
				5'd2:	begin	c1 <= 7'b1011_110;end
				5'd4:	begin	c1 <= 7'b0011_111;end
				5'd6:	begin	c1 <= 7'b1111_001;end
				5'd8:	begin	c1 <= 7'b0001_110;end
				5'd10:	begin	c1 <= 7'b0110_101;end
				5'd12:	begin	c1 <= 7'b0111_111;end
				5'd14:	begin	c1 <= 7'b1011_101;end
				5'd16:	begin	c1 <= 7'b1010_010;end
				5'd18:	begin	c1 <= 7'b0010_110;end
				5'd20:	begin	c1 <= 7'b1111_001;end
				5'd22:	begin	c1 <= 7'b0100_001;end

				5'd1:	begin	c1 <= 7'b0011_010;end
				5'd3:	begin	c1 <= 7'b1110_000;end
				5'd5:	begin	c1 <= 7'b0100_001;end
				5'd7:	begin	c1 <= 7'b1010_101;end
				5'd9:	begin	c1 <= 7'b0001_100;end
				5'd11:	begin	c1 <= 7'b0100_110;end
				5'd13:	begin	c1 <= 7'b1001_111;end
				5'd15:	begin	c1 <= 7'b1010_011;end
				5'd17:	begin	c1 <= 7'b1001_000;end
				5'd19:	begin	c1 <= 7'b1100_110;end
				5'd21:	begin	c1 <= 7'b1011_000;end
				5'd23:	begin	c1 <= 7'b1110_100;end
				default:begin	c1 <= 7'b0000_000;end
			endcase // squeeze_cnt
		end

	// always@(*)
	// 	begin
	// 		case(squeeze_cnt)
	// 			5'd0:	begin	c1 <= 8'b0000_0001;								c2 <= 8'b1011_0010;end
	// 			5'd1:	begin	c1 <= iterate ? 8'b1011_1010 : 8'b1011_0010;	c2 <= 8'b1011_0000;end
	// 			5'd2:	begin	c1 <= iterate ? 8'b0011_1011 : 8'b1011_1010;	c2 <= 8'b0100_0001;end
	// 			5'd3:	begin	c1 <= iterate ? 8'b1111_0001 : 8'b1011_0000;	c2 <= 8'b1010_1001;end
	// 			5'd4:	begin	c1 <= iterate ? 8'b0001_1010 : 8'b0011_1011;	c2 <= 8'b0001_1000;end
	// 			5'd5:	begin	c1 <= iterate ? 8'b0110_1001 : 8'b0100_0001;	c2 <= 8'b0100_1010;end
	// 			5'd6:	begin	c1 <= iterate ? 8'b0111_1011 : 8'b1111_0001;	c2 <= 8'b1001_1011;end
	// 			5'd7:	begin	c1 <= iterate ? 8'b1011_1001 : 8'b1010_1001;	c2 <= 8'b1010_0011;end
	// 			5'd8:	begin	c1 <= iterate ? 8'b1010_0010 : 8'b0001_1010;	c2 <= 8'b1001_0000;end
	// 			5'd9:	begin	c1 <= iterate ? 8'b0010_1010 : 8'b0001_1000;	c2 <= 8'b1100_1010;end
	// 			5'd10:	begin	c1 <= iterate ? 8'b1111_0001 : 8'b0110_1001;	c2 <= 8'b1011_0000;end
	// 			5'd11:	begin	c1 <= iterate ? 8'b0100_0001 : 8'b0100_1010;	c2 <= 8'b1110_1000;end
	// 			5'd12:	begin	c1 <= iterate ? 8'b0 		 : 8'b0111_1011;	c2 <= 8'b0;end
	// 			5'd13:	begin	c1 <= iterate ? 8'b0 		 : 8'b1001_1011;	c2 <= 8'b0;end
	// 			5'd14:	begin	c1 <= iterate ? 8'b0 		 : 8'b1011_1001;	c2 <= 8'b0;end
	// 			5'd15:	begin	c1 <= iterate ? 8'b0 		 : 8'b1010_0011;	c2 <= 8'b0;end
	// 			5'd16:	begin	c1 <= iterate ? 8'b0 		 : 8'b1010_0010;	c2 <= 8'b0;end
	// 			5'd17:	begin	c1 <= iterate ? 8'b0 		 : 8'b1001_0000;	c2 <= 8'b0;end
	// 			5'd18:	begin	c1 <= iterate ? 8'b0 		 : 8'b0010_1010;	c2 <= 8'b0;end
	// 			5'd19:	begin	c1 <= iterate ? 8'b0 		 : 8'b1100_1010;	c2 <= 8'b0;end
	// 			5'd20:	begin	c1 <= iterate ? 8'b0 		 : 8'b1111_0001;	c2 <= 8'b0;end
	// 			5'd21:	begin	c1 <= iterate ? 8'b0 		 : 8'b1011_0000;	c2 <= 8'b0;end
	// 			5'd22:	begin	c1 <= iterate ? 8'b0 		 : 8'b0100_0001;	c2 <= 8'b0;end
	// 			5'd23:	begin	c1 <= iterate ? 8'b0 		 : 8'b1110_1000;	c2 <= 8'b0;end
	// 			default:begin	c1 <= 8'b0000_0000;	c2 <= 8'b0000_0000;end
	// 		endcase // squeeze_cnt
	// 	end

//cnt/////////////////////////////////////////////////////////////
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			absorb_cnt <= 5'd0;
		else if(~enable)
			absorb_cnt <= 5'b0;
		else if(state == WORKING)
			absorb_cnt <= 5'b0;
		else if(read_en)
			absorb_cnt <= absorb_cnt + 1'b1;
		else
			absorb_cnt <= absorb_cnt;
	end

	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			squeeze_cnt <= 5'b0;
		else if(~enable)
			squeeze_cnt <= 5'b0;
		else if(state == WORKING)
			squeeze_cnt <= squeeze_cnt + 1'b1;
		else
			squeeze_cnt <= 5'b0;
	end

endmodule